package com.matrix.order.domain.factory;

import com.matrix.api.order.dto.CreateOrderDTO;
import com.matrix.api.order.dto.OrderItemDTO;
import com.matrix.api.product.vo.ProductVO;
import com.matrix.api.product.vo.SpecOptionVO;
import com.matrix.common.enums.OrderStatusEnum;
import com.matrix.common.exception.BusinessException;
import com.matrix.order.domain.model.Order;
import com.matrix.order.domain.model.OrderItem;
import com.matrix.order.infrastructure.gateway.acl.MerchantAcl;
import com.matrix.order.infrastructure.gateway.acl.ProductAcl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.RpcException;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 有点甜
 * @since 2025/8/2
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class OrderFactory {

    private final ProductAcl productAcl;
    private final MerchantAcl merchantAcl;

    public Order create(Long userId, CreateOrderDTO dto, String preAssignedOrderNo, BigDecimal platformDiscount, BigDecimal merchantDiscount) {
        merchantAcl.checkShopStatus(dto.getShopId());
        merchantAcl.checkTableAvailability(dto.getShopId(), dto.getAreaSeat());

        List<Long> productIds = dto.getItems().stream().map(OrderItemDTO::getProductId).toList();
        Map<Long, ProductVO> productMap = productAcl.getProductsByIds(productIds);

        List<OrderItem> orderItems = dto.getItems().stream()
                .map(itemDTO -> buildOrderItem(itemDTO, productMap))
                .toList();

        Map<Long, Integer> stockDeductionMap = orderItems.stream()
                .collect(Collectors.toMap(OrderItem::getProductId, OrderItem::getQuantity, Integer::sum));

        try {
            // productApi.deductStock(stockDeductionMap);
        } catch (RpcException e) {
            log.error("创建订单时扣减库存失败, RPC异常: {}", e.getMessage());
            throw new BusinessException("商品服务暂不可用，下单失败");
        }

        BigDecimal totalAmount = orderItems.stream()
                .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal discountAmount = platformDiscount.add(merchantDiscount);
        BigDecimal actualAmount = totalAmount.subtract(discountAmount);
        if (actualAmount.compareTo(BigDecimal.ZERO) < 0) {
            actualAmount = new BigDecimal("0.01");
        }

        Order order = new Order();
        order.setOrderNo(preAssignedOrderNo);
        order.setUserId(userId);
        order.setShopId(dto.getShopId());
        order.setShopOwnerId(merchantAcl.getOwnerIdByShopId(dto.getShopId()));
        order.setOriginalAmount(totalAmount);
        order.setDiscountAmount(discountAmount);
        order.setActualAmount(actualAmount);
        order.setTablewareQuantity(dto.getTablewareQuantity());
        order.setRemark(dto.getRemark());
        order.setAreaSeat(dto.getAreaSeat());
        order.setStatus(OrderStatusEnum.PROCESSING);
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());

        order.setUserCouponId(dto.getUserCouponId());
        order.setMerchantCouponId(dto.getMerchantCouponId());

        order.setItems(orderItems);

        return order;
    }

    private OrderItem buildOrderItem(OrderItemDTO itemDTO, Map<Long, ProductVO> productMap) {
        ProductVO product = productMap.get(itemDTO.getProductId());
        if (product == null) {
            throw new BusinessException("部分菜品已下架或不存在,请返回菜单刷新");
        }
        if (product.getStatus() != 1) {
            throw new BusinessException("菜品 [" + product.getName() + "] 已禁售");
        }

        BigDecimal itemPrice = product.getBasePrice();
        StringBuilder specsDesc = new StringBuilder();

        if (!CollectionUtils.isEmpty(itemDTO.getSpecOptionIds())) {
            Map<Long, SpecOptionVO> optionMap = product.getSpecs().stream()
                    .flatMap(group -> group.getOptions().stream())
                    .collect(Collectors.toMap(SpecOptionVO::getId, Function.identity()));

            for (Long optionId : itemDTO.getSpecOptionIds()) {
                SpecOptionVO option = optionMap.get(optionId);
                if (option != null) {
                    itemPrice = itemPrice.add(option.getPriceModifier());
                    if (!specsDesc.isEmpty()) {
                        specsDesc.append(",");
                    }
                    specsDesc.append(option.getName());
                } else {
                    log.warn("Order creation warning: SpecOptionId {} not found for ProductId {}.", optionId, product.getId());
                }
            }
        }

        OrderItem orderItem = new OrderItem();
        orderItem.setProductId(product.getId());
        orderItem.setProductName(product.getName());
        orderItem.setProductImageUrl(product.getImageUrl());
        orderItem.setSpecs(specsDesc.toString());
        orderItem.setPrice(itemPrice);
        orderItem.setQuantity(itemDTO.getQuantity());
        orderItem.setAdded(false);
        orderItem.setStatus(0);
        return orderItem;
    }
}
